package com.chenbei.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chenbei.common.domain.BaseEntity;
import com.chenbei.common.exception.LogicHandleException;
import com.chenbei.common.service.IFileUploadService;
import com.chenbei.common.domain.vo.UploadFileResult;
import com.chenbei.system.domain.*;
import com.chenbei.system.enums.SysMenuTypeEnum;
import com.chenbei.system.mapper.SysUserMapper;
import com.chenbei.system.service.*;
import com.chenbei.system.util.SysUserSelectorDataRespUtil;
import com.chenbei.system.vo.req.SysOrganizationReq;
import com.chenbei.system.vo.req.SysUserReq;
import com.chenbei.system.vo.resp.AdminUserMenuResp;
import com.chenbei.system.vo.resp.MenuMeta;
import com.chenbei.system.vo.resp.SysUserSelectorDataResp;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Value("${app.key}")
    private String appKey;

    @Value("${other.initPassword}")
    private String initPassword;

    @Autowired
    private ISysPostService sysPostService;

    @Autowired
    private ISysOrganizationService sysOrganizationService;

    @Autowired
    private ISysUserRoleService sysAdminUserRoleService;

    @Resource(name = "localFileUploadService")
    private IFileUploadService localFileUploadService;

    @Autowired
    private ISysMenuService menuService;

    @Autowired
    private ISysRoleMenuService sysRoleMenuService;

    @Autowired
    private ISysUserPostService sysUserPostService;

    @Autowired
    private ISysUserRoleService sysUserRoleService;

    @Autowired
    private ISysUserOrgService sysUserOrgService;
    @Autowired
    private SysMenuService sysMenuService;

    @Override
    public SysUser getByLoginName(String loginName) {
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (Validator.isMobile(loginName)) {
            lambdaQueryWrapper.eq(SysUser::getPhone, loginName);
        } else if (Validator.isEmail(loginName)) {
            lambdaQueryWrapper.eq(SysUser::getEmail, loginName);
        } else {
            lambdaQueryWrapper.eq(SysUser::getUsername, loginName);
        }
        lambdaQueryWrapper.last("Limit 1");
        return getOne(lambdaQueryWrapper);
    }

    @Override
    public SysUser detail(Long id) {
        SysUser sysUser = getById(id);

        List<SysRole> sysRoles = sysAdminUserRoleService.listByAdminUserId(id);
        sysUser.setRoles(sysRoles);

        if (sysRoles != null && !sysRoles.isEmpty()) {
            sysUser.setRoleIds(sysRoles.stream().map(SysRole::getId).collect(Collectors.toList()));
            List<Long> roleIds = sysRoles.stream().map(SysRole::getId).collect(Collectors.toList());
            List<SysMenu> sysMenus = sysRoleMenuService.listByRoleId(roleIds);
            List<AdminUserMenuResp> menus = buildAdminUserMenu(sysMenus, 0L);
            sysUser.setMenus(menus);
        }

        //岗位
        SysPost sysPost = sysPostService.getBySysUserId(id);
        if (sysPost != null) {
            sysUser.setPostId(sysPost.getId());
        }

        //组织
        SysOrganization organization = sysOrganizationService.getBySysUserId(id);
        if (organization != null) {
            sysUser.setOrgId(organization.getId());
            sysUser.setOrganization(organization);
        }

        return sysUser;
    }

    @Transactional
    @Override
    public SysUser add(SysUser sysUser) {
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysUser::getUsername, sysUser.getUsername())
                .last("limit 1");
        if (count(lambdaQueryWrapper) > 0) {
            throw new LogicHandleException("用户名已存在");
        }
        lambdaQueryWrapper.clear();
        if (StringUtils.isNoneBlank(sysUser.getPhone())) {
            lambdaQueryWrapper.eq(SysUser::getPhone, sysUser.getPhone())
                    .last("limit 1");
            if (count(lambdaQueryWrapper) > 0) {
                throw new LogicHandleException("手机号已存在");
            }
        }

        lambdaQueryWrapper.clear();
        if (StringUtils.isNoneBlank(sysUser.getEmail())) {
            lambdaQueryWrapper.eq(SysUser::getEmail, sysUser.getEmail())
                    .last("limit 1");
            if (count(lambdaQueryWrapper) > 0) {
                throw new LogicHandleException("邮箱已存在");
            }
        }

        sysUser.setPassword(encodePassword(sysUser.getPassword()));

        if(save(sysUser)) {
            addOrUpdateRefInfo(sysUser);
            return sysUser;
        } else {
            throw new LogicHandleException("用户添加失败");
        }
    }

    @Override
    public IPage<SysUser> page(SysUserReq sysUserReq) {
        Page<SysUser> pageConfig = new Page<>(sysUserReq.getPageNum(), sysUserReq.getPageSize());
        if (sysUserReq.getOrgId() != null && sysUserReq.getOrgId() > 0) {
            List<SysOrganization> sysOrganizations = sysOrganizationService.selfAndAllChildrenById(sysUserReq.getOrgId());
            if (sysOrganizations != null && !sysOrganizations.isEmpty()) {
                sysUserReq.setOrgIds(sysOrganizations.stream().map(SysOrganization::getId).collect(Collectors.toList()));
            }
        }
        return baseMapper.pageList(pageConfig, sysUserReq);
    }

    @Override
    public List<SysUser> list(SysUserReq sysUserReq) {
        return baseMapper.list(sysUserReq);
    }

    @Override
    public void update(SysUser sysUser) {
        LambdaUpdateWrapper<SysUser> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SysUser::getStatus, sysUser.getStatus())
                .set(SysUser::getPhone, sysUser.getPhone())
                .set(SysUser::getEmail, sysUser.getEmail())
                .set(SysUser::getSex, sysUser.getSex())
                .set(SysUser::getRemark, sysUser.getRemark())
                .set(SysUser::getNickname, sysUser.getNickname())
                .set(SysUser::getAvatar, sysUser.getAvatar())
                .set(BaseEntity::getUpdateTime, LocalDateTime.now())
                .eq(SysUser::getId, sysUser.getId());
        update(updateWrapper);
        addOrUpdateRefInfo(sysUser);
    }

    @Override
    @Transactional
    public void delete(long id) {
        if (id != 1 && id != StpUtil.getLoginIdAsLong()) {
            removeById(id);
            sysAdminUserRoleService.removeUserRoles(id);
        } else {
            throw new LogicHandleException("非法删除操作");
        }
    }

    @Override
    public void bindEmail(long id, String email) {
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysUser::getEmail, email)
                .ne(SysUser::getId, id);
        if (count(lambdaQueryWrapper) > 0) {
            throw new LogicHandleException("邮箱已绑定其他用户");
        }
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getEmail, email)
                .set(BaseEntity::getUpdateTime, LocalDateTime.now())
                .eq(SysUser::getId, id);
        update(lambdaUpdateWrapper);
    }

    @Override
    public void bindPhone(long id, String phone) {
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysUser::getPhone, phone)
                .ne(SysUser::getId, id);
        if (count(lambdaQueryWrapper) > 0) {
            throw new LogicHandleException("手机已绑定其他用户");
        }
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getPhone, phone)
                .eq(SysUser::getId, id);
        update(lambdaUpdateWrapper);
    }

    @Override
    public void modifyPassword(long id, String oldPassword, String newPassword) {
        SysUser sysUser = getById(id);
        if (!checkPassword(oldPassword, sysUser.getPassword())) {
            throw new LogicHandleException("旧密码和原密码不一致");
        }
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getPassword, encodePassword(newPassword))
                .eq(SysUser::getId, id);
        update(lambdaUpdateWrapper);
    }

    @Override
    public void resetPassword(long id) {
        if (id == 1 || id == StpUtil.getLoginIdAsLong()) {
            throw new LogicHandleException("不能重置管理员和自己的密码");
        }
        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(SysUser::getPassword, encodePassword(initPassword))
                .eq(SysUser::getId, id);
        update(lambdaUpdateWrapper);
    }

    @Override
    public String encodePassword(String sourcePassword) {
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, appKey.getBytes());
        return aes.encryptHex(sourcePassword);
    }

    @Override
    public boolean checkPassword(String sourcePassword, String encodePassword) {
        String sourcePassword1 = encodePassword(sourcePassword);
        return sourcePassword1.equals(encodePassword);
    }

    @Override
    public UploadFileResult uploadAvatar(MultipartFile file) {
        if (file.isEmpty()) {
            throw new LogicHandleException("上传文件不能为空");
        }
        if (file.getSize() > 1024*1024)  {
            throw new LogicHandleException("上传文件不能大于1M");
        }
        if (!Arrays.asList("jpg", "png", "jpeg").contains(FileUtil.extName(file.getOriginalFilename()))) {
            throw new LogicHandleException("上传文件格式只支持jpg、png、jpeg");
        }

        return localFileUploadService.upload(file);
    }

    @Override
    public List<AdminUserMenuResp> listAdminUserMenus(Long id) {
        List<SysRole> sysRoles = sysAdminUserRoleService.listByAdminUserId(id);
        if (sysRoles != null && !sysRoles.isEmpty()) {
            List<Long> roleIds = sysRoles.stream().map(SysRole::getId).collect(Collectors.toList());
            List<SysMenu> sysMenus;
            if (roleIds.contains(1L)) {
                sysMenus = sysMenuService.list();
            } else {
                sysMenus = sysRoleMenuService.listByRoleId(roleIds);
            }

            if (sysMenus != null && !sysMenus.isEmpty()) {
                sysMenus = sysMenus.stream().filter(sysMenu -> sysMenu.getStatus() == 1 && !Objects.equals(sysMenu.getType(), SysMenuTypeEnum.BUTTON.getType())).collect(Collectors.toList());
                return buildAdminUserMenu(sysMenus, 0L);
            }
        }
        return null;
    }

    @Override
    public List<SysUserSelectorDataResp> listSysUserSelectorData(Long orgPid) {
        List<SysUserSelectorDataResp> selectorDataRespList;

        //查询子组织
        SysOrganizationReq sysOrganizationReq = new SysOrganizationReq();
        sysOrganizationReq.setPid(orgPid);
        List<SysOrganization> organizations = sysOrganizationService.list(sysOrganizationReq);
        selectorDataRespList = SysUserSelectorDataRespUtil.convertSysOrganizations(organizations);

        //查询当前组织下的用户
        List<Long> roleIds = new ArrayList<>();
        if (orgPid <= 0) {
            roleIds.add(0L);
        }
        if (organizations != null && !organizations.isEmpty()) {
            for (SysOrganization sysOrganization : organizations) {
                roleIds.add(sysOrganization.getId());
            }
        }
        SysUserReq sysUserReq = new SysUserReq();
        sysUserReq.setOrgIds(roleIds);
        List<SysUser> sysUsers = this.list(sysUserReq);
        selectorDataRespList.addAll(SysUserSelectorDataRespUtil.convertSysUsers(sysUsers));

        return selectorDataRespList;
    }

    private List<AdminUserMenuResp> buildAdminUserMenu(List<SysMenu> sysMenus, Long pid) {
        List<AdminUserMenuResp> adminUserMenus = new ArrayList<>();
        for (SysMenu sysMenu : sysMenus) {
            if (Objects.equals(sysMenu.getPid(), pid)) {
                AdminUserMenuResp adminUserMenuResp = new AdminUserMenuResp();
                adminUserMenuResp.setId(sysMenu.getId());
                adminUserMenuResp.setPath(sysMenu.getPath());
                adminUserMenuResp.setIcon(sysMenu.getIcon());
                adminUserMenuResp.setType(sysMenu.getType());
                adminUserMenuResp.setComponent(sysMenu.getComponent());

                MenuMeta menuMeta = new MenuMeta();
                menuMeta.setLabel(sysMenu.getName());
                menuMeta.setVisible(sysMenu.getVisible());
                adminUserMenuResp.setMeta(menuMeta);

                adminUserMenus.add(adminUserMenuResp);
                adminUserMenuResp.setChildren(buildAdminUserMenu(sysMenus, sysMenu.getId()));
            }
        }

        return adminUserMenus;
    }

    /**
     * 添加或更新用户关联信息
     * @param sysUser 实例
     */
    private void addOrUpdateRefInfo(SysUser sysUser) {
        //岗位
        SysUserPost sysUserPost = new SysUserPost();
        sysUserPost.setSysUserId(sysUser.getId());
        sysUserPost.setPostId(sysUser.getPostId());
        sysUserPost.setCreateTime(LocalDateTime.now());
        sysUserPostService.addOrUpdate(sysUserPost);

        //角色
        sysUserRoleService.userBindRoles(sysUser.getId(), sysUser.getRoleIds());

        //组织
        SysUserOrg sysUserOrg = new SysUserOrg();
        sysUserOrg.setSysUserId(sysUser.getId());
        sysUserOrg.setOrgId(sysUser.getOrgId());
        sysUserOrg.setCreateTime(LocalDateTime.now());
        sysUserOrgService.addOrUpdate(sysUserOrg);
    }
}
